﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/8 11:37:00
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK;
using System.Windows.Forms;
using ImageK.Exceptions;
using ImageK.Gui;
using ImageK.Macros;
using ImageK.Plugin.Frame;
using ImageK.Text;
using ImageK.Util;
using static System.Windows.Forms.AxHost;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window;

namespace ImageK
{
    public class WindowManager
    {
        public static bool CheckForDuplicateName { get; set; }
        private static List<ImageWindow> imageList = new List<ImageWindow>();        // list of image windows
        private static List<ImageWindow> activations = new List<ImageWindow>();     // list of image windows, ordered by activation time
        private static ArrayList nonImageList = new ArrayList();	// list of non-image windows (Frames and Dialogs)

        private static ImageWindow currentWindow;
        private static Form frontWindow;
        private static Form frontTable;
        private static Form frontFrame;

        private static Hashtable _tempImageTable = new Hashtable();

        private WindowManager()
        {

        }

        /** Makes the image contained in the specified window the active image. */
        public static void setCurrentWindow(ImageWindow win)
        {
            if (win == null || win.isClosed() || win.getImagePlus() == null) // deadlock-"wait to lock"
                return;
            //IJ.log("setCurrentWindow: "+win.getImagePlus().getTitle()+" ("+(currentWindow!=null?currentWindow.getImagePlus().getTitle():"null") + ")");
            setWindow(win);
            _tempImageTable.Remove(Thread.CurrentThread);
            if (win == currentWindow || imageList.Count == 0)
                return;
            if (currentWindow != null)
            {
                // free up pixel buffers used by current window
                ImagePlus imp = currentWindow.getImagePlus();
                if (imp != null)
                {
                    if (!Prefs.keepUndoBuffers)
                        imp.trimProcessor();
                    imp.saveRoi();
                }
            }
            Undo.reset();
            currentWindow = win;
            activations.Remove(win);
            activations.Add(win);
            Menus.updateMenus();
            // if (Recorder.record && !IJ.isMacro())
            //     Recorder.record("selectWindow", win.getImagePlus().getTitle());
        }

        /** Returns the active ImageWindow. */
        public static ImageWindow getCurrentWindow()
        {
            return currentWindow;
        }

        static int getCurrentIndex()
        {
            return imageList.IndexOf(currentWindow);
        }


        /// <summary>
        /// Returns a reference to the active image or null if there isn't one.
        /// </summary>
        /// <returns></returns>
        public static ImagePlus getCurrentImage()
        {
            ImagePlus img = (ImagePlus)_tempImageTable[Thread.CurrentThread];
            //String str = (img==null)?" null":"";
            if (img == null)
                img = getActiveImage();
            //if (img!=null) IJ.log("getCurrentImage: "+img.getTitle()+" "+Thread.currentThread().hashCode()+str);
            return img;
        }

        /** Makes the specified image temporarily the active 
        image for this thread. Call again with a null
        argument to revert to the previous active image. */
        public static void setTempCurrentImage(ImagePlus img)
        {
            // IJ.log("setTempImage: "+(img!=null?""+img:"null")+" "+Thread.currentThread().hashCode());
            if (img == null)
                _tempImageTable.Remove(Thread.CurrentThread);
            else
                _tempImageTable[Thread.CurrentThread] = img;
        }
        /** Sets a temporary image for the specified thread. */
        public static void setTempCurrentImage(Thread thread, ImagePlus img)
        {
            if (thread==null)
                throw new RuntimeException("thread==null");
            if (img==null)
                _tempImageTable.Remove(thread);
            else
                _tempImageTable.Add(thread, img);
        }

        /** Returns the active ImagePlus. */
        private static ImagePlus getActiveImage()
        {
            if (currentWindow != null)
                return currentWindow.getImagePlus();
            else if (frontWindow != null && (frontWindow is ImageWindow))
                return frontWindow != null ? ((ImageWindow)frontWindow).getImagePlus() : null;
            else if (imageList.Count > 0)
            {
                ImagePlus imp = getFocusManagerActiveImage();
                if (imp != null)
                    return imp;
                ImageWindow win = (ImageWindow)imageList[imageList.Count - 1];
                return win.getImagePlus();
            }
            else
                return Interpreter.getLastBatchModeImage();
        }

        private static ImagePlus getFocusManagerActiveImage()
        {
            if (IJ.isMacro())
                return null;
            // KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();

            ImagePlus imp = null;
            var forms = Application.OpenForms;
            foreach (Form form in forms)
            {
                if (form.Visible)
                {
                    if (form is ImageWindow)
                        imp = ((ImageWindow)form).getImagePlus();
                    return imp;
                }
            }

            return imp;

        }

        /** Returns the number of open image windows. */
        public static int getWindowCount()
        {
            int count = imageList.Count;
            return count;
        }

        /** Returns the number of open images. */
        public static int getImageCount()
        {
            int count = imageList.Count;
            count += Interpreter.getBatchModeImageCount();
            if (count == 0 && getCurrentImage() != null)
                count = 1;
            return count;
        }

        /** Returns the front most window or null. */
        public static Form getActiveWindow()
        {
            return frontWindow;
        }

        /** Returns the Window containing the active table, or null.
         * @see ij.measure.ResultsTable#getActiveTable
        */
        public static Form getActiveTable()
        {
            return frontTable;
        }

        /** Obsolete; replaced by getActiveWindow. */
        public static Form getFrontWindow()
        {
            return frontFrame;
        }

        /** Returns a list of the IDs of open images. Returns
        null if no image windows are open. */
        public static int[] getIDList()
        {
            int nWindows = imageList.Count;
            int[] batchModeImages = Interpreter.getBatchModeImageIDs();
            int nBatchImages = batchModeImages.Length;
            if ((nWindows + nBatchImages) == 0)
                return null;
            int[] list = new int[nWindows + nBatchImages];
            for (int i = 0; i < nBatchImages; i++)
                list[i] = batchModeImages[i];
            int index = 0;
            for (int i = nBatchImages; i < nBatchImages + nWindows; i++)
            {
                ImageWindow win = (ImageWindow)imageList[index++];
                list[i] = win.getImagePlus().getID();
            }
            return list;
        }

        /** Returns a list of the titles of all open images. */
        public static string[] getImageTitles()
        {
            int[] list = getIDList();
            if (list==null)
                return new string[0];
            string[] titles = new string[list.Length];
            for (int i = 0; i<list.Length; i++)
            {
                ImagePlus img = getImage(list[i]);
                titles[i] = img.getTitle();
            }
            return titles;
        }

        /** Returns an array containing a list of the non-image Frames. */
        public static Form[] getNonImageWindows()
        {
            List<Form> list = new List<Form>();
            for (int i = 0; i<nonImageList.Count; i++)
            {
                object win = nonImageList[i];
                if (win is Form form)
                    list.Add(form);
            }
            Form[] frames = list.ToArray();
            return frames;
        }

        /** Returns an array containing a list of the non-image Frames and Dialogs. */
        public static Form[] getAllNonImageWindows()
        {
            List<Form> list = new List<Form>();
            for (int i = 0; i<nonImageList.Count; i++)
            {
                Object win = nonImageList[i];
                if (win is Form)
                list.Add((Form)win);
            }
            Form[] windows = new Form[list.Count];
            windows = list.ToArray();
            return windows;
        }

        /** Returns an array containing the titles of non-image Frames and Dialogs. */
        public static string[] getNonImageTitles()
        {
            List<string> list = new List<string>();
            for (int i = 0; i<nonImageList.Count; i++)
            {
                object win = nonImageList[i];
                // string title = win is Form?((Form)win).Text:((Dialog)win).getTitle();
                string title = win is Form?((Form)win).Text:"unknown";
                list.Add(title);
            }
            string[] titles = new String[list.Count];
            titles = list.ToArray();
            return titles;
        }

        /** For IDs less than zero, returns the ImagePlus with the specified ID. 
        Returns null if no open window has a matching ID or no images are open. 
        For IDs greater than zero, returns the Nth ImagePlus. Returns null if 
        the ID is zero. */
        public static ImagePlus getImage(int imageID)
        {
            if (imageID > 0)
                imageID = getNthImageID(imageID);
            if (imageID == 0 || getImageCount() == 0)
                return null;
            ImagePlus imp2 = Interpreter.getBatchModeImage(imageID);
            if (imp2 != null)
                return imp2;
            ImagePlus imp = null;
            for (int i = 0; i < imageList.Count; i++)
            {
                ImageWindow win = (ImageWindow)imageList[i];
                imp2 = win.getImagePlus();
                if (imageID == imp2.getID())
                {
                    imp = imp2;
                    break;
                }
            }
            imp2 = getCurrentImage();
            if (imp == null && imp2 != null && imp2.getID() == imageID)
                return imp2;
            return imp;
        }

        /** Returns the ID of the Nth open image. Returns zero if n<=0 
        or n greater than the number of open image windows. */
        public static int getNthImageID(int n)
        {
            if (n <= 0) return 0;
            if (Interpreter.isBatchMode())
            {
                int[] list = getIDList();
                if (n > list.Length)
                    return 0;
                else
                    return list[n - 1];
            }
            else
            {
                if (n > imageList.Count) return 0;
                ImageWindow win = (ImageWindow)imageList[n - 1];
                if (win != null)
                    return win.getImagePlus().getID();
                else
                    return 0;
            }
        }

        /** Returns the first image that has the specified title or null if it is not found. */
        public static ImagePlus getImage(string title)
        {
            int[] wList = getIDList();
            if (wList==null) return null;
            for (int i = 0; i<wList.Length; i++)
            {
                ImagePlus imp = getImage(wList[i]);
                if (imp!=null && imp.getTitle().Equals(title))
                    return imp;
            }
            return null;
        }

        /** Adds the specified window to the Window menu. */
        public static void addWindow(Form win)
        {
            if (win == null)
                return;
            else if (win is ImageWindow)
                addImageWindow((ImageWindow)win);
            else
            {
                //todo:
                // Menus.insertWindowMenuItem(win);
                // nonImageList.add(win);
            }
        }

        private static void addImageWindow(ImageWindow win)
        {
            ImagePlus imp = win.getImagePlus();
            if (imp == null) return;
            checkForDuplicateName(imp);
            imageList.Add(win);
            Menus.addWindowMenuItem(imp);
            setCurrentWindow(win);
        }

        public static void checkForDuplicateName(ImagePlus imp)
        {
            if (CheckForDuplicateName)
            {
                string name = imp.getTitle();
                if (isDuplicateName(name))
                    imp.setTitle(getUniqueName(name));
            }
            CheckForDuplicateName = false;
        }

        static bool isDuplicateName(string name)
        {
            if (name == null)
                return false;
            int n = imageList.Count;
            for (int i = 0; i < n; i++)
            {
                ImageWindow win = (ImageWindow)imageList[i];
                string name2 = win.getImagePlus().getTitle();
                if (name.Equals(name2))
                    return true;
            }
            return false;
        }

        /** Returns a unique name by adding, before the extension,  -1, -2, etc. as needed. */
        public static string getUniqueName(string name)
        {
            return getUniqueName(null, name);
        }

        public static string getUniqueName(ImagePlus imp, string name)
        {
            string name2 = name;
            string extension = "";
            int len = name2.Length;
            int lastDot = name2.LastIndexOf(".");
            if (lastDot != -1 && len - lastDot < 6 && lastDot != len - 1)
            {
                extension = name2.Substring(lastDot);
                name2 = name2.Substring(0, lastDot);
            }
            int lastDash = name2.LastIndexOf("-");
            len = name2.Length;
            if (imp != null && imp.getProp("UniqueName") == null)
                lastDash = -1;
            if (lastDash != -1 && len - lastDash < 4 && lastDash < len - 1 && Char.IsDigit(name2[lastDash + 1]) && name2[lastDash + 1] != '0')
                name2 = name2.Substring(0, lastDash);
            for (int i = 1; i <= 99; i++)
            {
                string name3 = name2 + "-" + i + extension;
                if (!isDuplicateName(name3))
                    return name3;
            }
            return name;
        }

        /** If 'name' is not unique, adds -1, -2, etc. as needed to make it unique. */
        public static String makeUniqueName(String name)
        {
            return isDuplicateName(name) ? getUniqueName(name) : name;
        }

        /** Removes the specified window from the Window menu. */
        public static void removeWindow(Form win)
        {
            if (win is ImageWindow)
                removeImageWindow((ImageWindow)win);
            else
            {
                int index = nonImageList.IndexOf(win);
                ImageK ij = IJ.GetInstance();
                if (index>=0)
                {
                    //todo:
                    // Menus.removeWindowMenuItem(index);
                    // nonImageList.removeElement(win);
                }
                if (win!=null && win==frontTable)
                    frontTable = null;
            }
            setWindow(null);
        }


        private static void removeImageWindow(ImageWindow win)
        {
            int index = imageList.IndexOf(win);
            if (index==-1)
                return;  // not on the window list
            try
            {
                // lock(this) {
                    imageList.Remove(win);
                // }
                activations.Remove(win);
                if (imageList.Count>1 && !Prefs.closingAll) {
                    ImageWindow win2 = activations.Count>0 ? (ImageWindow)activations[activations.Count-1] : null;
                    setCurrentWindow(win2);
                } else
                    currentWindow = null;
                setTempCurrentImage(null);  //???
                int nonImageCount = nonImageList.Count;
                if (nonImageCount>0)
                    nonImageCount++;

                //todo:
                // Menus.removeWindowMenuItem(nonImageCount+index);
                Menus.updateMenus();
                Undo.reset();
            }  catch (Exception e) { }
        }

        /** The specified frame becomes the front window, the one returnd by getFrontWindow(). */
        public static void setWindow(Form win)
        {
            frontWindow = win;
            frontFrame = win;
            if (win != null && win is TextWindow && !(win is Editor) && !"Log".Equals(((TextWindow)win).Text))
                frontTable = win;
            //System.out.println("Set window(F): "+(win!=null?win.getTitle():"null"));
        }

        /** Closes all windows. Stops and returns false if an image or Editor "save changes" dialog is canceled. */
        public static bool closeAllWindows()
        {
            Prefs.closingAll = true;
            //todo:
            // while (imageList.Count>0)
            // {
            //     if (!((ImageWindow)imageList[0]).close())
            //     {
            //         Prefs.closingAll = false;
            //         return false;
            //     }
            //     if (!quittingViaMacro())
            //         IJ.wait(100);
            // }
            // Prefs.closingAll = false;
            // Form[] nonImages = getNonImageWindows();
            // for (int i = 0; i<nonImages.Length; i++)
            // {
            //     Form frame = nonImages[i];
            //     if (frame!=null && frame is Commands)
            //     ((Commands)frame).close();
            //
            //     else if (frame!=null && (frame is Editor)) {
            //         ((Editor)frame).close();
            //         if (((Editor)frame).fileChanged())
            //             return false;
            //         if (!quittingViaMacro())
            //             IJ.wait(100);
            //     }
            // }
            // ImageK ij = IJ.GetInstance();
            // if (ij!=null && ij.quitting() && IJ.getApplet()==null)
            //     return true;
            // for (int i=0; i<nonImages.length; i++) {
            //     Frame frame = nonImages[i];
            //     if ((frame instanceof PlugInFrame) && !(frame instanceof Editor))
            //
            //     ((PlugInFrame) frame).close();
            //     else if (frame instanceof TextWindow)
            //
            //     ((TextWindow) frame).close();
            //     else {
            //         //frame.setVisible(false);
            //         frame.dispose();
            //     }
            // }
            return true;
        }


        // private static bool quittingViaMacro()
        // {
        //     ImageK ij = IJ.GetInstance();
        //     return ij!=null && ij.quittingViaMacro();
        // }
        //
        // /** Activates the next image window on the window list. */
        // public static void putBehind()
        // {
        //     if (IJ.debugMode) IJ.log("putBehind");
        //     if (imageList.size()<1 || currentWindow==null)
        //         return;
        //     int index = imageList.indexOf(currentWindow);
        //     ImageWindow win = null;
        //     int count = 0;
        //     do
        //     {
        //         index--;
        //         if (index<0) index = imageList.size()-1;
        //         win = (ImageWindow)imageList.get(index);
        //         if (++count==imageList.size()) return;
        //     } while (win instanceof HistogramWindow || win instanceof PlotWindow);
        //     if (win==null) return;
        //     ImagePlus imp = win.getImagePlus();
        //     if (imp!=null)
        //         IJ.selectWindow(imp.getID());
        // }

        /** Returns the temporary current image for this thread, or null. */
        public static ImagePlus getTempCurrentImage()
        {
            return (ImagePlus)_tempImageTable[Thread.CurrentThread];
        }

        /** Returns the window (a Frame or a Dialog) with the specified
        title,  or null if a window with that title is not found. */
        public static Form getWindow(string title)
        {
            for (int i = 0; i < nonImageList.Count; i++)
            {
                object win = nonImageList[i];
                string winTitle = ((Form)win).Text;
                if (title.Equals(winTitle))
                    return (Form)win;
            }
            return getImageWindow(title);
        }


        /** Obsolete; replaced by getWindow(). */
        public static Form getFrame(string title)
        {
            for (int i = 0; i<nonImageList.Count; i++)
            {
                object win = nonImageList[i];
                string winTitle = win is Form?((Form)win).Text:null;
                if (title.Equals(winTitle))
                    return (Form)win;
            }
            Form frame = getImageWindow(title);
            if (frame==null)
            {
                Form win = getWindow(title);
                if (win!=null)
                    frame = new Form()
                    {
                        Text = "Proxy"
                    };
            }
            return frame;
        }

        private static Form getImageWindow(String title)
        {
            int[] wList = getIDList();
            int len = wList != null ? wList.Length : 0;
            for (int i = 0; i < len; i++)
            {
                ImagePlus imp = getImage(wList[i]);
                if (imp != null)
                {
                    if (imp.getTitle().Equals(title))
                        return imp.getWindow();
                }
            }
            return null;
        }

//         /** Activates a window selected from the Window menu. */
//         synchronized static void activateWindow(String menuItemLabel, MenuItem item)
//         {
//             for (int i = 0; i<nonImageList.size(); i++)
//             {
//                 Object win = nonImageList.get(i);
//                 String title = win instanceof Frame?((Frame)win).getTitle():((Dialog)win).getTitle();
//                 if (menuItemLabel.equals(title))
//                 {
//                     if (win instanceof Frame)
// 					toFront((Frame)win);
//
//                 else
//                 ((Dialog)win).toFront();
//             ((CheckboxMenuItem)item).setState(false);
//             if (Recorder.record && !IJ.isMacro())
//                 Recorder.record("selectWindow", title);
//             return;
//         }
//     }
//     int lastSpace = menuItemLabel.lastIndexOf(' ');
// 		if (lastSpace>0) // remove image size (e.g., " 90K")
// 		menuItemLabel = menuItemLabel.substring(0, lastSpace);
// 		String idString = item.getActionCommand();
//     int id = (int)Tools.parseDouble(idString, 0);
//     ImagePlus imp = WindowManager.getImage(id);
// 		if (imp==null) return;
// 		ImageWindow win1 = imp.getWindow();
// 		if (win1==null) return;
// 		setCurrentWindow(win1);
//     toFront(win1);
//     int index = imageList.indexOf(win1);
//     int n = Menus.window.getItemCount();
//     int start = Menus.WINDOW_MENU_ITEMS+Menus.windowMenuItems2;
// 		for (int j=start; j<n; j++) {
// 			MenuItem mi = Menus.window.getItem(j);
// 			if (mi instanceof CheckboxMenuItem)
// 				((CheckboxMenuItem) mi).setState((j-start)==index);						
// 		}
// 	}
//     
//     /** Repaints all open image windows. */
//     public synchronized static void repaintImageWindows()
// {
//     int[] list = getIDList();
//     if (list==null) return;
//     for (int i = 0; i<list.length; i++)
//     {
//         ImagePlus imp2 = getImage(list[i]);
//         if (imp2!=null)
//         {
//             imp2.setTitle(imp2.getTitle()); // update "(G)" flag (global calibration)
//             ImageWindow win = imp2.getWindow();
//             if (win!=null) win.repaint();
//         }
//     }
// }
//
// public static void showList()
// {
//     for (int i = 0; i<imageList.size(); i++)
//     {
//         ImageWindow win = (ImageWindow)imageList.get(i);
//         ImagePlus imp = win.getImagePlus();
//         IJ.log(i + " " + imp.getTitle() + (win==currentWindow ? "*" : "")+" "+imp.getID());
//     }
//     for (int i = 0; i<activations.size(); i++)
//     {
//         ImageWindow win = (ImageWindow)activations.get(i);
//         ImagePlus imp = win.getImagePlus();
//         IJ.log(i + " " + imp.getTitle() + " " + imp.getID());
//     }
//     if (imageList.size()==0) IJ.log("imageList is empty");
//     if (activations.size()==0) IJ.log("activations list is empty");
//     IJ.log(" ");
// }

        public static void toFront(Form frame)
        {
            if (frame==null) return;
            if (frame.WindowState == FormWindowState.Minimized)
                frame.WindowState = FormWindowState.Normal;
            frame.BringToFront();
        }
    }
}
